Using Modeless Dialog Boxes
To display a modeless dialog box, you can create the dialog box by callingGetNewDialog
. Then you can respond to user actions in the dialog box by intercepting dialog-related events in your main event loop and handling those events. The Dialog Manager calls the Control Manager to draw any controls you've put in the dialog box and handle user actions in them. If the dialog box contains any application-defined user items, you need to provide the Dialog Manager with a drawing procedure so that it knows how to draw the items. You also need to handle user actions for any such application-defined items yourself.Creating a Modeless Dialog Box
You can create a modeless dialog box by callingGetNewDialog
and passing it the resource ID of an appropriate'DLOG'
resource. The Venn Diagrammer application supports only one modeless dialog box, in which the user can set various application preferences. Venn Diagrammer displays that dialog box after the user chooses the Preferences command from the Venn menu.
iGetVennPrefs: DoModelessDialog(rVennDPrefsDial, gPrefsDialog);As you can see, Venn Diagrammer simply calls the application-defined procedureDoModelessDialog
, passing it a resource ID specifying the dialog box to open and a global variable in which to return the dialog pointer created byGetNewDialog
.
Listing 7-2 defines theDoModelessDialog
procedure.Listing 7-2 Creating a modeless dialog box
PROCEDURE DoModelessDialog (myKind: Integer; VAR myDialog: DialogPtr); VAR myPointer: Ptr; BEGIN IF myDialog = NIL THEN {the dialog box doesn't exist yet} BEGIN myPointer := NewPtr(sizeof(DialogRecord)); IF myPointer = NIL THEN exit(DoModelessDialog); myDialog := GetNewDialog(myKind, myPointer, WindowPtr(-1)); IF myDialog <> NIL THEN BEGIN DoSetupUserItems(myKind, myDialog); {set up user items} DoSetupCtrlValues(myDialog); {set up initial values} END; END ELSE BEGIN ShowWindow(myDialog); SelectWindow(myDialog); SetPort(myDialog); END; END;TheDoModelessDialog
procedure first determines whether the specified dialog box has already been created, by checking the value of the global variable passed to it. If the variable contains any value other thanNIL
, the dialog box already exists (but is perhaps hidden or obscured by other windows). If so,DoModelessDialog
simply makes the dialog box visible (by callingShowWindow
), makes it the active window (by callingSelectWindow
), and establishes it as the current graphics port (by callingSetPort
).If, however, the specified dialog box doesn't exist yet, then
DoModelessDialog
allocates memory for a new dialog record and (if successful) callsGetNewDialog
, passing it the appropriate resource ID. IfGetNewDialog
returns successfully (as indicated by a returned dialog pointer whose value isn'tNIL
),DoModelessDialog
then calls two application-defined routines,DoSetupUserItems
andDoSetupCtrlValues
, to tell the Dialog Manager how draw the user items in the dialog box and to set the correct initial values for the dialog box's radio buttons and checkboxes.Setting Up Application-Defined Items
Whenever a modeless dialog box contains application-defined user items, you need to tell the Dialog Manager how to draw them. You do this by calling the Dialog Manager procedureSetDialogItem
for each application-defined item in the dialog box.
Listing 7-3 shows theDoSetupUserItems
procedure called byDoModelessDialog
(defined in Listing 7-2).Listing 7-3 Setting up application-defined dialog items
PROCEDURE DoSetupUserItems (myKind: Integer; VAR myDialog: DialogPtr); VAR myType: Integer; myHand: Handle; myRect: Rect; count: Integer; origPort: GrafPtr; BEGIN GetPort(origPort); SetPort(myDialog); CASE myKind OF rVennDPrefsDial: FOR count := 1 TO kVennPrefsItemCount DO IF count IN [iExist1Icon..iExist4Icon, iEmpty1Icon..iEmpty4Icon] THEN BEGIN GetDialogItem(myDialog, count, myType, myHand, myRect); SetDialogItem(myDialog, count, myType, @DoUserItem, myRect); END; OTHERWISE ; END; SetPort(origPort); END;TheDoSetupUserItems
procedure simply selects the relevant application-defined items, retrieves information about each item (by callingGetDialogItem
), and then callsSetDialogItem
to associate a particular application-defined drawing procedure with each item. As you can see, the drawing procedure (DoUserItem
) is the same for each user item in the Preferences dialog box. This is possible because the Dialog Manager passes the drawing procedure the dialog pointer and item number when it wants a particular item to be drawn. Listing 7-4 defines the Venn Diagrammer procedure that draws user items.Listing 7-4 Drawing application-defined dialog items
PROCEDURE DoUserItem (myDialog: DialogPtr; myItem: Integer); VAR myType: Integer; myHand: Handle; myRect: Rect; origPort: GrafPtr; BEGIN GetPort(origPort); SetPort(myDialog); GetDialogItem(myDialog, myItem, myType, myHand, myRect); IF myDialog = gPrefsDialog THEN CASE myItem OF iExist1Icon..iExist4Icon: BEGIN DoPlotIcon(myRect, GetIcon(kExistID + myItem - iExist1Icon), myDialog, srcCopy); END; iEmpty1Icon..iEmpty4Icon: BEGIN DoPlotIcon(myRect, GetIcon(kEmptyID + myItem - iEmpty1Icon), myDialog, srcCopy); FrameRect(myRect); END; OTHERWISE ; END; {CASE} SetPort(origPort); {restore original port} END;TheDoUserItem
procedure is also fairly simple. It makes sure that the dialog pointer passed to it picks out the Preferences dialog box. Then it calls the application-defined procedureDoPlotIcon
(defined in Listing 5-8 on page 101) to draw the appropriate part of an icon in the item rectangle. If the emptiness patterns are being drawn,DoUserItem
also draws a box around the pattern (by callingFrameRect
).Handling User Actions in a Modeless Dialog Box
The Venn Diagrammer application calls itsDoHandleDialogEvent
function for each event it retrieves from the Event Manager. Its strategy is to determine if the returned event applies to a dialog box. If so,DoHandleDialogEvent
handles the event and returnsTRUE
to indicate that it did so; otherwise,DoHandleDialogEvent
just returnsFALSE
to indicate that it didn't handle the event. Listing 7-5 definesDoHandleDialogEvent
. (See Listing 4-4 on page 77 to see whenDoHandleDialogEvent
is called.)Listing 7-5 Handling events in a modeless dialog box
FUNCTION DoHandleDialogEvent (myEvent: EventRecord): Boolean; VAR eventHandled: Boolean; {did we handle the event?} myDialog: DialogPtr; myItem: Integer; BEGIN eventHandled := FALSE; IF FrontWindow <> NIL THEN IF IsDialogEvent(myEvent) THEN IF DialogSelect(myEvent, myDialog, myItem) THEN BEGIN eventHandled := TRUE; SetPort(myDialog); IF myDialog = gPrefsDialog THEN BEGIN CASE myItem OF iEmpty1Radio..iEmpty4Radio: gEmptyIndex := myItem; iEmpty1Icon..iEmpty4Icon: gEmptyIndex := myItem - 4; iExist1Radio..iExist4Radio: gExistIndex := myItem - iEmpty4Icon; iExist1Icon..iExist4Icon: gExistIndex := myItem - (iEmpty4Icon + 4); iGetNextRandomly: gStepRandom := NOT gStepRandom; iAutoAdjust: gAutoAdjust := NOT gAutoAdjust; iShowSchoolNames: gShowNames := NOT gShowNames; iUseExistImport: gGiveImport := NOT gGiveImport; iSaveVennPrefs: DoSavePrefs; OTHERWISE ; END; DoSetupCtrlValues(myDialog); {update values} END; END; DoHandleDialogEvent := eventHandled; END;TheDoHandleDialogEvent
function calls the Dialog Manager'sIsDialogEvent
function to determine whether at the time of the event the frontmost window is a dialog box. If not, thenDoHandleDialogEvent
just exits and returns the valueFALSE
. If, however, the event did occur while a dialog box was active, then the event might apply to that dialog box. To determine whether it does apply,DoHandleDialogEvent
calls the Dialog Manager'sDialogSelect
function, which handles most of the events relating to a dialog box. For example, if the event is an update or activate event for the dialog box,DialogSelect
updates or activates the dialog box and returnsFALSE
(to indicate that no further processing is required by the calling application).If the event involves an enabled item in the dialog box,
DialogSelect
returns a function result ofTRUE
. In the myItem parameter, it returns the item number of the item selected by the user. In themyDialog
parameter, it returns a pointer to the dialog record for the dialog box where the event occurred. In all other cases, theDialogSelect
function returnsFALSE
. WhenDialogSelect
returnsTRUE
, you should do whatever is appropriate as a response to the event involving that item in that particular dialog box; when it returnsFALSE
, you should do nothing.The
DoHandleDialogEvent
function uses a very simple technique for handling user selections of items in the Preferences dialog box. As you can see, it sets the appropriate application global variables for clicks of the radio buttons, and it toggles the appropriate global variables for clicks of the checkboxes. ThenDoHandleDialogEvent
calls the application-defined procedureDoSetupCtrlValues
to change the values of those controls, turning the radio buttons and checkboxes off or on, as appropriate. Listing 7-6 gives the definition ofDoSetupCtrlValues
.Listing 7-6 Setting the state of radio buttons and checkboxes
PROCEDURE DoSetupCtrlValues (myDialog: DialogPtr); VAR count: Integer; myType: Integer; myHand: Handle; myRect: Rect; origPort: GrafPtr; BEGIN IF myDialog = NIL THEN exit(DoSetupCtrlValues); GetPort(origPort); {save the current graphics port} SetPort(myDialog); {always do this before drawing} ShowWindow(myDialog); IF myDialog = gPrefsDialog THEN BEGIN FOR count := 1 TO kVennPrefsItemCount DO BEGIN GetDialogItem(myDialog, count, myType, myHand, myRect); IF myType = ctrlItem + radCtrl THEN CASE count OF iExist1Radio..iExist4Radio: SetCtlValue(ControlHandle(myHand), ORD(gExistIndex = count - (iExist1Radio - 1))); iEmpty1Radio..iEmpty4Radio: SetCtlValue(ControlHandle(myHand), ORD(gEmptyIndex = count - (iEmpty1Radio - 1))); OTHERWISE ; END; IF myType = ctrlItem + chkCtrl THEN CASE count OF iGetNextRandomly: SetCtlValue(ControlHandle(myHand), ORD(gStepRandom = TRUE)); iShowSchoolNames: SetCtlValue(ControlHandle(myHand), ORD(gShowNames = TRUE)); iUseExistImport: SetCtlValue(ControlHandle(myHand), ORD(gGiveImport = TRUE)); iAutoAdjust: SetCtlValue(ControlHandle(myHand), ORD(gAutoAdjust = TRUE)); OTHERWISE ; END; END; END; SetPort(origPort); {restore the previous graphics port} END;TheDoSetupCtrlValues
procedure simply calls the Control Manager procedureSetCtlValue
to set the value of each control in the dialog box according to the value of some global variable. This makes it easy to toggle checkboxes and to group radio buttons in such a way that exactly one radio button in each group is on.
- IMPORTANT
- The strategy for handling dialog box events described in this section might not be the best or most efficient strategy for your application. For a more complete discussion of handling dialog box events, see the chapter "Dialog Manager" in Inside Macintosh: Macintosh Toolbox Essentials.
![]()